package cn.codenest;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;


/**
 * create by: Hyman
 * description: netty服务端
 * create time: 2021/4/27
 */
public class Server {


    private final int port;

    public Server(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        Server server=new Server(9988);
        server.start();
    }

    private void start() throws InterruptedException {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        /*1、EventGroup是为每个注册到Selector中地channel分别的事件处理器，可以注册感兴趣的事件、将事件委派给EchoServerHandler等等
         * 2、所有的EventGroup本身只有一个线程驱动，无需考虑同步
         * */
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });
            /*调用sync实现阻塞，直到绑定完成*/
            ChannelFuture f = b.bind().sync();
            /*获取closeFuture，并阻塞当前线程直到它完成*/
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }

    }

    /**
     * create by: Hyman
     * description:
     * 使用netty实现地回声服务端
     * (1) Sharable标示一个ChannelHandler可以被多个Channel安全地共享
     * (2) Netty内部使用回调来处理事件，当一个回调被触发时，相关事件就可以被ChannelHandler地实现处理，ChannelInboundHandlerAdapter就是一个ChannelHandler实现
     * create time: 2021/4/27
     */
    @ChannelHandler.Sharable
    class EchoServerHandler extends ChannelInboundHandlerAdapter {

        /**
         * create by: Hyman
         * description:channelRead()每个传入地消息都会调用
         * create time: 2021/4/27
         */
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf in = (ByteBuf) msg;
            System.out.println("服务端接受到地消息是: " + in.toString(CharsetUtil.UTF_8));
            /*将接受到地消息写给发送者，而不冲涮出站消息*/
            ctx.write(in);
        }

        /**
         * create by: Hyman
         * description: 通知ChannelInboundHandlerAdapter在最后一次调用channelRead后调用，是当前读取地最后一条消息
         * create time: 2021/4/27
         */
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            /*将暂存在ChannelOutboundBuffer中地消息，存到套接字中*/
            System.out.println("channelReadComplete");
            ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }

        /**
         * create by: Hyman
         * description: 在读取操作期间，有异常抛出时调用
         * create time: 2021/4/27
         */
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }

    }
}
